class Product < ActiveRecord::Base
  belongs_to :brand
  has_many :categories, :through => :product_categories
  has_many :product_categories, :dependent => :destroy
  has_many :product_colors, :dependent => :destroy

  accepts_nested_attributes_for :categories, :product_colors, :allow_destroy => true

  validates :model, :presence => { :message => 'Debe ingresar un modelo.' }
  validates :price, :numericality => { :greater_than_or_equal_to => 0, :message => 'Debe ingresar un precio mayor o igual a cero.' }, :presence => { :message => 'Debe ingresar un precio válido.' }
  validates :promotional_price, :numericality => { :greater_than_or_equal_to => 0, :message => 'Debe ingresar un precio promocional mayor o igual a cero.' }, :unless => Proc.new { |p| p.promotional_price.blank? }
  validates :branch_price, :numericality => { :greater_than_or_equal_to => 0, :message => 'Debe ingresar un precio para Franquicias mayor o igual a cero.' }, :presence => { :message => 'Debe ingresar un precio para Franquicias válido.' }
  validates :branch_promotional_price, :numericality => { :greater_than_or_equal_to => 0, :message => 'Debe ingresar un precio promocional para Franquicias mayor o igual a cero.' }, :unless => Proc.new { |p| p.branch_promotional_price.blank? }

  default_scope where('deleted = 0 and display = 1').order('model ASC')
  #scope :at_home, where("show_home > 0").order("id").limit(5)
  #scope :not_deleted, where("deleted = 0").order('model ASC')

  scope :filter_by_brand, lambda { |brand_id|
    where(:brand_id => brand_id)
  }

  scope :filter_by_categories, lambda { |category_ids|
    joins(:categories).where(:categories => { :id => category_ids }).group('products.id').having("count(product_categories.category_id) = #{category_ids.count}")
  }


  # Paperclip
  has_attached_file :photo, :styles => { :home => "210x269#", :list => "192x192#", :detail => "337x337#", :cart => "162x192#", :checkout => "50x50#" }
  #has_attached_file :photo, :styles => { :home => "210x269>", :list => "192x192>", :detail => "337x337>", :cart => "162x192>", :checkout => "50x50>" }

  # Pagination settings
  self.per_page = configatron.pagination_catalog_product_per_page

  def self.not_deleted
    self.where("deleted = 0").order('model ASC')
  end

  def self.at_home
    self.where("show_home > 0").order("id").limit(5)
  end

  def update_categories(categ_ids)
    # Converts category array to array of Int
    categ_ids = categ_ids.map{ |c| c.to_i }
    # Categories to remove
    remove_cat = self.categories.map(&:id) - categ_ids

    # Categories to add
    cats_to_add = categ_ids - self.categories.map(&:id)

    # Removes categories
    remove_cat.each do |cat_id|
      cat = ProductCategory.where("product_id = ? and category_id = ?", self.id, cat_id).first
      cat.delete
    end

    # Adds categories
    cats_to_add.each do |cat_id|
      cat = Category.find(cat_id)
      self.categories << cat
    end
  end

  def update_colors(color_ids)
    # Converts Color array to array of Int
    color_ids = color_ids.map{ |c| c.to_i }

    # Colors to remove
    remove_colors = self.colors.map(&:id) - color_ids

    # Colors to add
    colors_to_add = color_ids - self.colors.map(&:id)

    # Removes colors
    remove_colors.each do |color_id|
      color = ProductColor.where("product_id = ? and color_id = ?", self.id, color_id).first
      color.delete
    end

    # Adds colors
    colors_to_add.each do |color_id|
      color = Color.find(color_id)
      self.colors << color
    end
  end

  def self.filter(filters)
    products = nil

    if !filters[:brand_id].blank? && !filters[:category_ids].blank?
      # Both Brand and Categories are present
      products = Product.filter_by_brand(filters[:brand_id]).filter_by_categories(filters[:category_ids])

    elsif !filters[:brand_id].blank?
      # Only Brand is present
      products = Product.filter_by_brand(filters[:brand_id])
    elsif !filters[:category_ids].blank?
      # Only Categories is present
      products = Product.filter_by_categories(filters[:category_ids])
    else
      # None of them is present
      products = Product
    end

    products
  end

  #def self.filter_by_categories(category_ids)
    # Filters products by selected categories
  #  Product.joins(:categories).where(:categories => { :id => category_ids }).group('products.id').having("count(product_categories.category_id) = #{category_ids.count}")
  #end

  def get_price(kvn_branch)
    # If the product has a promotional price defined, that will be the price to use.
    if kvn_branch
      (branch_promotional_price.blank? || branch_promotional_price == 0) ? branch_price : branch_promotional_price
    else
      (promotional_price.blank? || promotional_price == 0) ? price : promotional_price
    end
  end

  def available?
    # An available product is the one that is not:
    # - Sold out
    # - Hidden
    # - Deleted
    !self.deleted? && self.display? && !is_sold_out?
  end

  def self.search(keywords)
    Product.where("model like ?", "%#{keywords}%")
  end

  def self.new_from_csv(brand,model,picture_name,price, branch_price,promotional_price,branch_promotional_price,colors,show_at_home,sold_out,visible,acero,bijou,caucho,cronografos,cuero,metal,despertador,varios, dama, caballero)
    begin
      product = Product.new

      product.brand = Brand.find_by_name(brand.to_s.strip)
      product.model = model.to_s.strip
      product.price = price.to_s.to_d
      product.branch_price = branch_price.to_s.to_d
      product.promotional_price = promotional_price.to_s.to_d
      product.branch_promotional_price = branch_promotional_price.to_s.to_d

      # Creates associated product colors
      unless colors.blank?
        colors.split(";").each do |c|
          product.product_colors.build(:color_name => c.to_s.strip)
          #  product.product_colors.build(:color_code => c.split("-")[0].to_s.strip, :color_name => c.split("-")[1].to_s.strip)
        end
      end

      # Uses internal_name to find product's photo
      file_path = File.join(configatron.product_photos_dir, picture_name)
      product.photo = open(file_path)

      product.show_home = show_at_home.to_i
      product.sold_out = sold_out.to_i
      product.display = visible.to_i

      categories = {
                      :acero => acero.to_i,
                      :bijou => bijou.to_i,
                      :caucho => caucho.to_i,
                      :cronografos => cronografos.to_i,
                      :cuero => cuero.to_i,
                      :metal => metal.to_i,
                      :despertador => despertador.to_i,
                      :varios => varios.to_i,
                      :dama => dama.to_i,
                      :hombre => caballero.to_i
                   }

      # Selects only the ones that were selected
      categories = categories.select { |k, v| v == 1 }

      product.assign_categories(categories)

      if product.valid?
        product.save
      else
        raise Exception.new
      end
    rescue Exception => e
      raise e
    end
  end

  def assign_categories(categories)
    unless categories.blank?
      categories.each do |c|
        cat_name = c[0].to_s
        db_cat = Category.where("lower(name) = ?", cat_name.downcase)

        if db_cat.blank?
          db_cat = Category.create!(:name => cat_name.camelcase)
        else
          db_cat = db_cat.first
        end

        self.product_categories.build(:category_id => db_cat.id)
      end
    end
  end

  def mark_as_deleted
    self.update_attributes(:deleted => true)
  end

  def update_sold_out_colors(color_ids = nil)
    product_sold_out = true

    self.product_colors.each do |color|
      if !color_ids.blank? && color_ids.include?(color.id.to_s)
        # It's been marked as sold out.
        color.update_attribute(:sold_out, true)
      else
        product_sold_out = false
        color.update_attribute(:sold_out, false)
      end
    end

    if product_sold_out == true
      self.update_sold_out_product
    end
  end

  def update_sold_out_product
    if self.sold_out?
      self.update_attribute(:sold_out, false)
    else
      self.update_attribute(:sold_out, true)
    end
  end

  def is_sold_out?
    if self.product_colors.blank?
      sold_out = self.sold_out?
    else
      sold_out = self.product_colors.select { |color| !color.sold_out? }.count == 0
    end

    sold_out
  end

  def mark_as_sold_out(colors = nil)
    self.update_sold_out_colors(colors)
=begin
    unless colors.blank?
      self.update_sold_out_colors(colors)
    else
      self.update_sold_out_product
    end
=end
  end

  def get_discount_percentage
    discount = (1- self.promotional_price / self.price) * 100

    "-#{discount.to_i}%"
  end

  def unisex?
    man_cat = Category.find_by_name('Hombre')
    woman_cat = Category.find_by_name('Dama')
    self.categories.include?(man_cat && woman_cat)
  end
end
